import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';

import '../../config.dart'; // 自己配置的后端地址等
import '../../model/network/dynamic_map.dart';
import '../store/user_store.dart';
import '../utils/toast.dart';

class RequestService {
  static final RequestService _instance = RequestService._internal();

  factory RequestService() => _instance;

  late final Dio dio;
  BuildContext? _context;

  RequestService._internal() {
    dio = Dio(
      BaseOptions(
        baseUrl: Config.baseUrl,
        connectTimeout: const Duration(seconds: 15),
        receiveTimeout: const Duration(seconds: 15),
        headers: {
          'Content-Type': 'application/json',
        },
        // 允许 200 ~ 499 返回，不自动抛错，由我们拦截器处理
        validateStatus: (status) {
          return status != null && status >= 200 && status < 500;
        },
      ),
    );

    dio.interceptors.add(_TokenInterceptor());
    dio.interceptors.add(_ResponseInterceptor(() => _context));
  }

  /// 设置当前页面上下文，供拦截器中弹窗使用
  void setContext(BuildContext context) {
    _context = context;
  }

  Future<dynamic> get(String path, {Map<String, dynamic>? queryParameters}) async {
    final response = await dio.get(path, queryParameters: queryParameters);
    return response.data;
  }

  Future<dynamic> post(String path, {Map<String, dynamic>? data}) async {
    final response = await dio.post(path, data: data);
    return response.data;
  }

  Future<dynamic> put(String path, {dynamic data}) async {
    final response = await dio.put(path, data: data);
    return response.data;
  }

  Future<dynamic> delete(String path, {dynamic data}) async {
    final response = await dio.delete(path, data: data);
    return response.data;
  }

  Future<dynamic> uploadFile(
    String path, {
    required String fieldName,
    required String filePath,
    Map<String, dynamic>? data,
  }) async {
    // 动态推断文件 MIME 类型
    final mimeType = lookupMimeType(filePath) ?? 'application/octet-stream';
    final mimeParts = mimeType.split('/'); // e.g. ['image', 'jpeg']

    final formData = FormData.fromMap({
      if (data != null) ...data,
      fieldName: await MultipartFile.fromFile(
        filePath,
        contentType: MediaType(mimeParts[0], mimeParts[1]),
      ),
    });

    final response = await dio.post(path, data: formData);
    return response.data;
  }

  Future<dynamic> uploadFiles(
    String path, {
    required String fieldName,
    required List<String> filePaths,
    Map<String, dynamic>? data,
  }) async {
    // 先构造多个 MultipartFile，带上 mime 类型
    final files = await Future.wait(filePaths.map((filePath) async {
      final mimeType = lookupMimeType(filePath) ?? 'application/octet-stream';
      final mimeParts = mimeType.split('/');

      return MultipartFile.fromFile(
        filePath,
        contentType: MediaType(mimeParts[0], mimeParts[1]),
      );
    }));

    final formData = FormData.fromMap({
      if (data != null) ...data,
      fieldName: files,
    });

    final response = await dio.post(path, data: formData);
    return response.data;
  }
}

/// 自动添加 Token 的请求拦截器
class _TokenInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    final loginDataStr = Store().getString('login_data');
    if (loginDataStr.isNotEmpty) {
      final Map<String, dynamic> loginData = jsonDecode(loginDataStr);
      final token = loginData['token'] ?? '';
      options.headers['Authorization'] = 'Bearer $token';
    }
    super.onRequest(options, handler);
  }
}

/// 响应拦截器，统一处理 HTTP 状态码和业务 code 错误，弹窗提示
class _ResponseInterceptor extends Interceptor {
  _ResponseInterceptor(BuildContext? Function() param0);

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    final dynamic data = DynamicMap(response.data);
    final statusCode = response.statusCode ?? 0;
    if (statusCode >= 400) {
      final dynamic message = data.message;
      String msg;

      if (message is String) {
        msg = message;
      } else if (message is List) {
        msg = message.join(', ');
      } else {
        msg = '请求错误';
      }
      toast(msg);

      return handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          error: msg,
          response: response,
          type: DioExceptionType.badResponse,
        ),
      );
    }
    final dynamic code = data.code ?? 0;
    final dynamic msg = data.message ?? '未知错误';
    if (code != 200) {
      toast(msg);
      return handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          error: msg,
          response: response,
          type: DioExceptionType.badResponse,
        ),
      );
    }
    response.data = data;
    handler.next(response);
  }
}
